/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/* 
 * Copyright (c) 2009 City University of Hong Kong
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License m_version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author: Wilson Thong (wilsonwk@ee.cityu.edu.hk) 
 */
#include "point-to-point-graph.h"
#include "ns3/point-to-point-helper.h"
#include "ns3/log.h"
#include "ns3/node-list.h"
#include <sstream>

NS_LOG_COMPONENT_DEFINE ("PointToPointGraph");

namespace ns3 {
namespace eecityu {

NS_OBJECT_ENSURE_REGISTERED (GraphNode);
NS_OBJECT_ENSURE_REGISTERED (GraphLink);

////////////////////////////////////
// GraphLink class implementation //
////////////////////////////////////

TypeId 
GraphLink::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::eecityu::GraphLink")
    .SetParent<Object> ()
    ;

  return tid;
}

GraphLink::GraphLink () : m_nodeLocal (NULL), m_nodeRemote (NULL)
{}

GraphLink::GraphLink (Ptr<GraphNode> n1, Ptr<GraphNode> n2) 
{
  SetNode (n1, n2);
}

bool 
GraphLink::operator< (const GraphLink& glink) const
{
  Ptr<GraphNode> rightFirstGn;  
  Ptr<GraphNode> rightSecondGn; 
  Ptr<GraphNode> leftFirstGn;   
  Ptr<GraphNode> leftSecondGn; 

  if (*glink.GetNodeLocal () < *glink.GetNodeRemote ()) {
    rightFirstGn = glink.GetNodeLocal ();
    rightSecondGn = glink.GetNodeRemote ();
  } else {
    rightFirstGn = glink.GetNodeRemote ();
    rightSecondGn = glink.GetNodeLocal ();
  }
  if (*m_nodeLocal < *m_nodeRemote) {
    leftFirstGn = m_nodeLocal;
    leftSecondGn = m_nodeRemote;
  } else {
    leftFirstGn = m_nodeRemote;
    leftSecondGn = m_nodeLocal;
  }

  pair<GraphNode, GraphNode> rightglink (*rightFirstGn, *rightSecondGn);
  pair<GraphNode, GraphNode> leftglink  (*leftFirstGn , *leftSecondGn);

  //// from pair objects for doing comparsion
  //GraphNode rightFirstGn  = min (*glink.GetNodeLocal (), *glink.GetNodeRemote ());
  //GraphNode rightSecondGn = max (*glink.GetNodeLocal (), *glink.GetNodeRemote ());
  //GraphNode leftFirstGn   = min (*m_nodeLocal, *m_nodeRemote);
  //GraphNode leftSecondGn  = max (*m_nodeLocal, *m_nodeRemote);
  //pair<GraphNode, GraphNode> rightglink (rightFirstGn, rightSecondGn);
  //pair<GraphNode, GraphNode> leftglink  (leftFirstGn , leftSecondGn);

  return leftglink < rightglink;
}

bool 
GraphLink::operator>  (const GraphLink& gl) const
{
  return gl < (*this);
}

bool 
GraphLink::operator== (const GraphLink& gl) const
{
  return !((*this) < gl) && !(gl < (*this));
}

bool 
GraphLink::operator!= (const GraphLink& gl) const
{
  return !((*this) == gl);
}

bool 
GraphLink::operator<= (const GraphLink& gl) const
{
  return ((*this) < gl) || ((*this) == gl);
}

bool 
GraphLink::operator>= (const GraphLink& gl) const
{
  return (gl < (*this)) || ((*this) == gl);
}

void 
GraphLink::SetNode (Ptr<GraphNode> local, Ptr<GraphNode> remote)
{
  m_nodeLocal = local;
  m_nodeRemote = remote;
}

void 
GraphLink::SwapLocalRemoteNodes ()
{
  Ptr<GraphNode> temp = GetNodeLocal ();
  m_nodeLocal = GetNodeRemote ();
  m_nodeRemote = temp;
}

Ptr<GraphNode> 
GraphLink::GetNodeLocal () const
{
  return m_nodeLocal;
}

Ptr<GraphNode> 
GraphLink::GetNodeRemote () const
{
  return m_nodeRemote;
}

Ptr<PointToPointChannel> 
GraphLink::GetPointToPointChannel () const
{
  NS_LOG_FUNCTION_NOARGS ();

  NS_ASSERT_MSG (m_nodeLocal != NULL, "From-node is not set for this link");
  NS_ASSERT_MSG (m_nodeRemote != NULL, "To-node is not set for this link");

  // get the ns-3 Node object assoicated with the m_nodeRemote and m_nodeLocal GraphNode's
  Ptr<Node> nsNodeFrom = m_nodeLocal->GetNode ();
  Ptr<Node> nsNodeTo = m_nodeRemote->GetNode ();

  NS_ASSERT_MSG (nsNodeFrom != NULL, "From-node does not have an associated ns-3 Node object");
  NS_ASSERT_MSG (nsNodeTo != NULL, "To-node does not have an associated ns-3 Node object");

  // get the number of devices attached to the from-node and to-node
  uint32_t fromNDevices = nsNodeFrom->GetNDevices ();
  uint32_t toNDevices = nsNodeTo->GetNDevices ();

  // search the net device on the node which has fewer node degree, so that the searching time is minimized
  uint32_t searchNDevices = 0;
  Ptr<Node> searchNsNode = NULL;
  if (fromNDevices > toNDevices) {
    searchNDevices = toNDevices;
    searchNsNode = nsNodeTo;
  } else {
    searchNDevices = fromNDevices;
    searchNsNode = nsNodeFrom;
  }

  NS_ASSERT_MSG (searchNDevices != 0, "The GraphLink does not have an associated ns-3 NetDevice object");

  // search for the ns-3 point to point channel object associated with this graphical link
  Ptr<PointToPointChannel> channel = NULL;
  bool found = false;
  for (uint32_t i=1; i<=searchNDevices && !found; i++) {
    // get a channel and get its two nodes
    Ptr<PointToPointNetDevice> tempDev = DynamicCast<PointToPointNetDevice> (searchNsNode->GetDevice (i));
    channel = DynamicCast<PointToPointChannel> (tempDev->GetChannel ());    
    Ptr<PointToPointNetDevice> dev1 = channel->GetPointToPointDevice (0);
    Ptr<PointToPointNetDevice> dev2 = channel->GetPointToPointDevice (1);
    Ptr<Node> n1 = dev1->GetNode ();
    Ptr<Node> n2 = dev2->GetNode ();
    Ptr<GraphNode> gn1 = n1->GetObject<GraphNode> ();
    Ptr<GraphNode> gn2 = n1->GetObject<GraphNode> ();

    NS_ASSERT_MSG (gn1, "GraphNode object not found from an ns-3 node " << n1);
    NS_ASSERT_MSG (gn2, "GraphNode object not found from an ns-3 node " << n2);

    // check if the two nodes are the same as the two nodes associated with this link
    found = found || ((*gn1 == *m_nodeRemote) && (*gn2 == *m_nodeLocal));
    found = found || ((*gn1 == *m_nodeLocal) && (*gn2 == *m_nodeRemote));
  }

  NS_ASSERT_MSG (found, "There is not a ns3 point to point channel object associated with this graph link");

  return channel;
}

ostream& 
operator<< (ostream& os, const GraphLink& glink)
{
  os << "{" << *glink.GetNodeLocal () << ", " << *glink.GetNodeRemote () << "}";
  return os;
}

////////////////////////////////////
// GraphNode class implementation //
////////////////////////////////////

TypeId 
GraphNode::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::eecityu::GraphNode")
    .SetParent<Object> ()
    ;

  return tid;
}


GraphNode::GraphNode () : m_name (), m_node ()
{}

GraphNode::GraphNode (string name) : m_node ()
{
  SetName (name);
}

GraphNode::GraphNode (string name, Ptr<Node> n) : m_name () 
{
  SetName (name);
  SetNode (n);
}

bool 
GraphNode::operator< (const GraphNode& gn) const
{
  bool ret;

  // the comparsion includes the Node ID also, if Node objects are associated to both GraphNodes
  if ((m_node != NULL) && (gn.m_node != NULL)) {    
    if (m_name != gn.m_name) 
      ret = m_name < gn.m_name;
    else 
      ret = m_node->GetId () < gn.m_node->GetId ();
  } else { // otherwise, the comparsion only compares GraphNode's name of the both GraphNodes
    ret = m_name < gn.m_name;
  }

  return ret;
}

bool 
GraphNode::operator> (const GraphNode& gn) const
{
  return gn < (*this);
}

bool 
GraphNode::operator== (const GraphNode& gn) const
{
  return !((*this) < gn) && !(gn < (*this));
}

bool 
GraphNode::operator!= (const GraphNode& gn) const
{
  return !((*this) == gn);
}

void 
GraphNode::SetName (string name)
{
  m_name = name;
}

string 
GraphNode::GetName () const
{
  return m_name;
}

void 
GraphNode::SetNode (Ptr<Node> n)
{
  m_node = n;
}

Ptr<Node> 
GraphNode::GetNode () const
{
  return m_node;
}

Ptr<GraphLink> 
GraphNode::GetGraphLink (int i) const
{
  Ptr<PointToPointNetDevice> tempDev = DynamicCast<PointToPointNetDevice> (m_node->GetDevice (i));
  Ptr<PointToPointChannel> channel = DynamicCast<PointToPointChannel> (tempDev->GetChannel ());
  Ptr<PointToPointNetDevice> dev1 = channel->GetPointToPointDevice (0);
  Ptr<PointToPointNetDevice> dev2 = channel->GetPointToPointDevice (1);
  Ptr<Node> n1 = dev1->GetNode ();
  Ptr<Node> n2 = dev2->GetNode ();
  Ptr<GraphNode> gn1 = n1->GetObject<GraphNode> ();
  Ptr<GraphNode> gn2 = n2->GetObject<GraphNode> ();

  Ptr<GraphLink> retglink;
  if ((*this) == *gn1) 
    retglink = CreateObject<GraphLink> (gn1, gn2);
  else if ((*this) == *gn2)
    retglink = CreateObject<GraphLink> (gn2, gn1);
  else
    NS_ASSERT_MSG (false, "The GraphLink obtained from a node does not attach to this node!");

  NS_LOG_DEBUG ("Got a GraphLink " << *retglink);
  return retglink;
}

uint32_t 
GraphNode::GetNGraphLinks () const
{
  return m_node->GetNDevices ();
}

uint32_t 
GraphNode::GetId () const
{
  return m_node->GetId ();
}

ostream& 
operator<< (ostream& os, const GraphNode& gn)
{
  os << gn.GetName ();
  return os;
}

////////////////////////////////////////////
// PointToPointGraph class implementation //
////////////////////////////////////////////

PointToPointGraph::PointToPointGraph (void)
{}

bool 
PointToPointGraph::GraphNodeLess::operator() (const Ptr<GraphNode>& n1, const Ptr<GraphNode>& n2) const
{
  return (*n1) < (*n2);
}

bool PointToPointGraph::GraphLinkLess::operator() (const Ptr<GraphLink>& glink1, const Ptr<GraphLink>& glink2) const 
{
  return (*glink1) < (*glink2);
}

bool 
PointToPointGraph::AddLink (string name1, string name2)
{
  NS_LOG_FUNCTION (name1 << name2);
  
  // create two graph node objects from the given node names
  Ptr<GraphNode> gn1 = CreateObject<GraphNode> (name1);
  Ptr<GraphNode> gn2 = CreateObject<GraphNode> (name2);
  
  // try inserting the two given nodes into the topology and see if there are any duplication
  pair<set<Ptr<GraphNode> >::iterator, bool> insertGn1Ret, insertGn2Ret;
  insertGn1Ret = m_graphNodeSet.insert (gn1);
  insertGn2Ret = m_graphNodeSet.insert (gn2);
  bool gn1IsNew = insertGn1Ret.second;
  bool gn2IsNew = insertGn2Ret.second;

  // associate the correct ns3 node objects to the two newly created graph node objects
  if (gn1IsNew) {
    gn1->SetNode (CreateObject<Node> ());
    gn1->GetNode ()->AggregateObject (gn1);    
  } else {
    gn1 = *insertGn1Ret.first;
  }
  if (gn2IsNew) {
    gn2->SetNode (CreateObject<Node> ());
    gn2->GetNode ()->AggregateObject (gn2);
  } else {
    gn2 = *insertGn2Ret.first;
  }

  bool retBool;
  // create a new link between the two given nodes only if such a link does not yet exist
  if (!GetLink (gn1, gn2)) {
    Ptr<Node> n1 = gn1->GetNode ();
    Ptr<Node> n2 = gn2->GetNode ();
    PointToPointHelper ppHelper;
    ppHelper.Install (n1, n2);
    NS_LOG_DEBUG ("The link " << GraphLink (gn1, gn2) << " is added");
    retBool = true;
  } else {
    NS_LOG_WARN ("The link " << GraphLink (gn1, gn2) << " is not added as it already exists");
    retBool = false;
  }
  return retBool;
}

Ptr<GraphNode> 
PointToPointGraph::GetNode (string name) const
{
  GraphNodeSet_t::const_iterator iter;
  Ptr<GraphNode> ret;

  iter = m_graphNodeSet.find (CreateObject<GraphNode> (name));
  if (iter == m_graphNodeSet.end ())
    ret = NULL;
  else
    ret = *iter;

   return ret;
}

Ptr<GraphLink> 
PointToPointGraph::GetLink (Ptr<GraphNode> gn1, Ptr<GraphNode> gn2) const
{
  NS_LOG_FUNCTION (*gn1 << *gn2);

  // the graph link being searched
  Ptr<GraphLink> glink = CreateObject<GraphLink> (gn1, gn2);

  // search through all Channel objects
  bool found = false;
  for (GraphNodeSet_t::const_iterator it = m_graphNodeSet.begin (); it != m_graphNodeSet.end () && !found; it++) {
    Ptr<GraphNode> gnode = *it;
    for (uint32_t i=0; i<gnode->GetNGraphLinks () && !found; i++) {
      Ptr<GraphLink> _glink = gnode->GetGraphLink (i);
      // stop looping if the link is found
      if (*_glink == *glink) {
        // make sure the GraphLink returned will have its local node being equal to gn1
        if (*glink->GetNodeLocal () != *_glink->GetNodeLocal ())
          _glink->SwapLocalRemoteNodes ();
        // returning _glink instead of the glink, as the nodes of _glink are aggregated with GraphNode but those of glink are not
        glink = _glink;

        found = true;
      }
    }
  }

  // return the result
  Ptr<GraphLink> ret;
  if (found) {
    ret = glink;
    NS_LOG_DEBUG ("The link " << *glink << " is found");
  } else {
    ret = NULL;
    NS_LOG_DEBUG ("The link " << *glink << " is not found. NULL pointer is returned");
  }
  return ret;
}

Ptr<GraphLink> 
PointToPointGraph::GetLink (const char* name1, const char* name2) const
{
  return GetLink (CreateObject<GraphNode> (name1), CreateObject<GraphNode> (name2));
}

uint32_t 
PointToPointGraph::GetNodesN () const
{
  return m_graphNodeSet.size ();
}

class _GraphLinkLess
{
public:
  /// \brief To sort Ptr<GraphNode> objects according to their names. This class is used when defining the sorting order of some STL containers
  bool operator() (const Ptr<GraphLink>& glink1, const Ptr<GraphLink>& glink2) const {
    return (*glink1) < (*glink2);
  }
};

ostream& 
operator<< (ostream& os, const PointToPointGraph& bt)
{
  typedef PointToPointGraph::GraphNodeSet_t GraphNodeSet_t;
  typedef set<Ptr<GraphLink>, _GraphLinkLess> GraphLinkSet_t;
  GraphLinkSet_t graphLinkSet;

  // get all GraphLink objects and insert them into a STL set container
  bt.GetAllLinks (insert_iterator<GraphLinkSet_t> (graphLinkSet, graphLinkSet.end ()));

  // print out all graph links
  bool printSeperator = false;
  for (GraphLinkSet_t::const_iterator it = graphLinkSet.begin (); it != graphLinkSet.end (); it++) {
    Ptr<GraphLink> glink = *it;
    if (printSeperator) os << ", ";
    os << *glink;
    printSeperator = true;
  }

  return os;
}

///////////////////////////////
// Test Suite implementation //
///////////////////////////////

class MultiGraphTestCase : public TestCase
{
public:
  MultiGraphTestCase ();
  virtual bool DoRun ();
};

PointToPointGraphTestCase::PointToPointGraphTestCase ()
  : TestCase ("Test PointToPointGraph class functionality")
{}

GraphNodeTestCase::GraphNodeTestCase ()
  : TestCase ("Test GraphNode class functionality")
{}

GraphLinkTestCase::GraphLinkTestCase ()
  : TestCase ("Test GraphLink class functionality")
{}

MultiGraphTestCase::MultiGraphTestCase ()
  : TestCase ("Test 2 PointToPointGraphs coexisting")
{}

bool 
PointToPointGraphTestCase::DoRun ()
{
  PointToPointGraph g;  

  bool addLinkRet = g.AddLink ("a", "b");
  NS_TEST_ASSERT_MSG_EQ (addLinkRet, true, "Adding link {a,b} failed");
  Ptr<GraphLink> getLinkRet = g.GetLink ("a", "b");
  NS_TEST_ASSERT_MSG_NE (getLinkRet, NULL, "Failed getting back the link {a,b} after it is added");
  getLinkRet = g.GetLink ("b", "a");
  NS_TEST_ASSERT_MSG_NE (getLinkRet, NULL, "Failed getting back the link {b,a} after it is added");

  addLinkRet = g.AddLink ("b", "c");
  NS_TEST_ASSERT_MSG_EQ (addLinkRet, true, "Adding link {a,c} failed");
  getLinkRet = g.GetLink ("b", "c");
  NS_TEST_ASSERT_MSG_NE (getLinkRet, NULL, "Failed getting back the link {b,c} after it is added");
  getLinkRet = g.GetLink ("c", "b");
  NS_TEST_ASSERT_MSG_NE (getLinkRet, NULL, "Failed getting back the link {c,b} after it is added");

  addLinkRet = g.AddLink ("a", "c");
  NS_TEST_ASSERT_MSG_EQ (addLinkRet, true, "Adding link {a,b} failed");
  getLinkRet = g.GetLink ("a", "c");
  NS_TEST_ASSERT_MSG_NE (getLinkRet, NULL, "Failed getting back the link {a,c} after it is added");
  getLinkRet = g.GetLink ("c", "a");
  NS_TEST_ASSERT_MSG_NE (getLinkRet, NULL, "Failed getting back the link {c,a} after it is added");

  addLinkRet = g.AddLink ("b", "a");
  NS_TEST_EXPECT_MSG_EQ (addLinkRet, false, "falsely added the duplicated link {b,a}");
  addLinkRet = g.AddLink ("a", "b");
  NS_TEST_EXPECT_MSG_EQ (addLinkRet, false, "falsely added the duplicated link {a,b}");

  NS_TEST_EXPECT_MSG_NE (g.GetNode ("a"), NULL, "GetNode () failed returning the node 'a'");
  NS_TEST_EXPECT_MSG_NE (g.GetNode ("b"), NULL, "GetNode () failed returning the node 'b'");
  NS_TEST_EXPECT_MSG_NE (g.GetNode ("c"), NULL, "GetNode () failed returning the node 'c'");
  NS_TEST_EXPECT_MSG_EQ (g.GetNode ("d"), NULL, "There is no node 'd' but GetNode () returns some non-zero value");
  NS_TEST_EXPECT_MSG_EQ (g.GetNode ("e"), NULL, "There is no node 'e' but GetNode () returns some non-zero value");
  NS_TEST_EXPECT_MSG_EQ (g.GetNode ("f"), NULL, "There is no node 'f' but GetNode () returns some non-zero value");

  NS_TEST_EXPECT_MSG_EQ (g.m_graphNodeSet.size (), 3, "Two nodes added from the AddLink() are missing from the PointToPointGraph");
  NS_TEST_EXPECT_MSG_EQ (g.GetNodesN (), 3, "GetNodesN() does not return the number of nodes correctly");

  typedef set< Ptr<GraphLink> > t_SetGraphLink;
  t_SetGraphLink links;
  g.GetAllLinks (insert_iterator<t_SetGraphLink>(links, links.end ()));
  NS_TEST_EXPECT_MSG_EQ (links.size (), 3, "GetAllLinks () does not return the correct number of GraphLinks");

  string expectedStr = "{a, b}, {a, c}, {b, c}";
  ostringstream ActualOss; 
  ActualOss << g << ends;
  string actualStr (ActualOss.str ().c_str ());
  NS_TEST_EXPECT_MSG_EQ (actualStr, expectedStr, "The string out of the PointToPointGraph is incorrect");

  return false;
}

bool 
GraphNodeTestCase::DoRun ()
{ 
  GraphNode gn_abc;
  gn_abc.SetName ("abc");
  NS_TEST_EXPECT_MSG_EQ (gn_abc.m_name, "abc", "The SetName () is failed to set the name of a GraphNode");
  NS_TEST_EXPECT_MSG_EQ (gn_abc.GetName (), "abc", "The node name returned by GetName () is incorrect");

  GraphNode gn_bcd ("bcd");
  NS_TEST_EXPECT_MSG_EQ (gn_bcd.m_name, "bcd", "The constructor is failed to set the name of a GraphNode");
  NS_TEST_EXPECT_MSG_EQ (gn_bcd.GetName (), "bcd", "The node name returned by GetName () is incorrect");

  NS_TEST_EXPECT_MSG_EQ ((gn_abc <  gn_bcd), true,  "The operator< () does not return the expected result");
  NS_TEST_EXPECT_MSG_EQ ((gn_abc >  gn_bcd), false, "The operator> () does not return the expected result");
  NS_TEST_EXPECT_MSG_EQ ((gn_abc == gn_bcd), false, "The operator== () does not return the expected result");
  NS_TEST_EXPECT_MSG_EQ ((gn_abc <  gn_abc), false, "The operator< () does not return the expected result");
  NS_TEST_EXPECT_MSG_EQ ((gn_abc >  gn_abc), false, "The operator> () does not return the expected result");
  NS_TEST_EXPECT_MSG_EQ ((gn_abc == gn_abc), true,  "The operator== () does not return the expected result");

  Ptr<Node> n = CreateObject<Node> ();
  gn_abc.SetNode (n);
  NS_TEST_EXPECT_MSG_EQ (gn_abc.m_node->GetId (), n->GetId (), "The SetNode () is failed associating a Node object to the GraphNode");
  NS_TEST_EXPECT_MSG_EQ (gn_abc.GetNode ()->GetId (), n->GetId (), "The node returned by GetNode () is incorrect");

  NS_TEST_EXPECT_MSG_EQ (gn_abc.GetId (), n->GetId (), "The GetId () does not return the correct value");

  NS_TEST_EXPECT_MSG_EQ (gn_abc.GetNGraphLinks (), 0, "These is no links attached to the GraphNode but GetNGraphLinks () returns some no-zero value");

  ostringstream actualOss;
  actualOss << gn_abc;
  string actualStr (actualOss.str ().c_str ());
  string expectStr ("abc");
  NS_TEST_EXPECT_MSG_EQ (actualStr, expectStr, "GraphNode is printed as a string incorrectly");

  PointToPointGraph g;
  g.AddLink ("a", "b");
  g.AddLink ("a", "c");
  g.AddLink ("a", "d");
  g.AddLink ("b", "d");
  Ptr<GraphNode> n_a = g.GetNode ("a");
  NS_TEST_EXPECT_MSG_EQ (n_a->GetNGraphLinks (), 3, "GetNGraphLinks () does not return the correct degree of node 'a'");

  NS_TEST_EXPECT_MSG_NE (n_a->GetGraphLink (0), NULL, "GetGraphLink (0) does not return a GraphLink of node 'a'");
  NS_TEST_EXPECT_MSG_NE (n_a->GetGraphLink (1), NULL, "GetGraphLink (1) does not return a GraphLink of node 'a'");
  NS_TEST_EXPECT_MSG_NE (n_a->GetGraphLink (2), NULL, "GetGraphLink (2) does not return a GraphLink of node 'a'");

  return false;
}

bool 
GraphLinkTestCase::DoRun ()
{
  PointToPointGraph g;
  g.AddLink ("a", "b");
  g.AddLink ("b", "c");
  g.AddLink ("c", "a");

  Ptr<GraphNode> gn_a = CreateObject<GraphNode> ("a");
  Ptr<GraphNode> gn_b = CreateObject<GraphNode> ("b");
  Ptr<GraphLink> glink = g.GetLink ("a", "b");
  NS_TEST_EXPECT_MSG_EQ (*glink->GetNodeLocal (), *gn_a, "The GetNodeLocal () does not return the correct GraphNode");
  NS_TEST_EXPECT_MSG_EQ (*glink->GetNodeRemote (), *gn_b, "The GetNodeRemote () does not return the correct GraphNode");
  glink = g.GetLink ("b", "a");
  NS_TEST_EXPECT_MSG_EQ (*glink->GetNodeLocal (), *gn_b, "The GetNodeLocal () does not return the correct GraphNode");
  NS_TEST_EXPECT_MSG_EQ (*glink->GetNodeRemote (), *gn_a, "The GetNodeRemote () does not return the correct GraphNode");

  ostringstream actualOss;
  actualOss << *glink << ends;
  string actualStr (actualOss.str ().c_str ());
  string expectStr ("{b, a}");
  NS_TEST_EXPECT_MSG_EQ (actualStr, expectStr, "The GraphLink is printed as string incorrectly");

  Ptr<GraphLink> glink_ab = CreateObject<GraphLink> (gn_a, gn_b);
  Ptr<GraphLink> glink_ba = CreateObject<GraphLink> (gn_b, gn_a);
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab < *glink_ba), false, "GraphLink operator< does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab > *glink_ba), false, "GraphLink operator< does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab == *glink_ba), true, "GraphLink operator== does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab != *glink_ba), false, "GraphLink operator!= does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab <= *glink_ba), true, "GraphLink operator<= does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab >= *glink_ba), true, "GraphLink operator>= does not return a correct result");

  Ptr<GraphLink> glink_bc = g.GetLink ("b", "c");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab < *glink_bc), true, "GraphLink operator< does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab > *glink_bc), false, "GraphLink operator> does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab == *glink_bc), false, "GraphLink operator== does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab != *glink_bc), true, "GraphLink operator!= does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab >= *glink_bc), false, "GraphLink operator>= does not return a correct result");
  NS_TEST_EXPECT_MSG_EQ ((*glink_ab <= *glink_bc), true, "GraphLink operator<= does not return a correct result");

  Ptr<GraphLink> glink_bc2 = CreateObject<GraphLink> (glink_bc->GetNodeLocal (), glink_bc->GetNodeRemote ());
  NS_TEST_EXPECT_MSG_EQ ((*glink_bc == *glink_bc2), true, "GraphLink operator== does not return a correct result");

  return false;
}

bool MultiGraphTestCase::DoRun ()
{
  PointToPointGraph ga;
  ga.AddLink ("a", "b");
  ga.AddLink ("b", "c");
  ga.AddLink ("c", "a");

  PointToPointGraph gb;
  gb.AddLink ("a", "b");
  gb.AddLink ("b", "c");
  gb.AddLink ("c", "a");

  Ptr<GraphLink> ga_ab = ga.GetLink ("a", "b");
  Ptr<GraphLink> gb_ab = gb.GetLink ("a", "b");
  Ptr<GraphLink> glink_ab = CreateObject<GraphLink> (CreateObject<GraphNode> ("a"), CreateObject<GraphNode> ("b"));
  NS_TEST_EXPECT_MSG_EQ ((*ga_ab == *gb_ab), false, "Two links from different topologies cannot be equal, although their node names are the same");
  NS_TEST_EXPECT_MSG_EQ ((*ga_ab == *glink_ab), true, "A link from a topology is expected to be the same as the link created by hand, but Graphlink operator== does not return the correct result");
  NS_TEST_EXPECT_MSG_EQ ((*gb_ab == *glink_ab), true, "A link from a topology is expected to be the same as the link created by hand, but Graphlink operator== does not return the correct result");

  Ptr<GraphNode> ga_a = ga_ab->GetNodeLocal ();
  Ptr<GraphNode> gb_a = gb_ab->GetNodeLocal ();
  Ptr<GraphNode> gnode_a = CreateObject<GraphNode> ("a");
  NS_TEST_EXPECT_MSG_EQ ((*ga_a == *gb_a), false, "Two nodes from different topologies cannot be equal, although their name are the same");
  NS_TEST_EXPECT_MSG_EQ ((*ga_a == *gnode_a), true, "A node from a topology is expected to be the same as the node created by hand, but GraphNode operator == does not return the correct result");
  NS_TEST_EXPECT_MSG_EQ ((*gb_a == *gnode_a), true, "A node from a topology is expected to be the same as the node created by hand, but GraphNode operator == does not return the correct result");

  return false;
}

BaseTopologyTestSuite::BaseTopologyTestSuite ()
  : TestSuite ("point-to-point-graph", UNIT)
{
  AddTestCase (new GraphNodeTestCase ());
  AddTestCase (new GraphLinkTestCase ());
  AddTestCase (new PointToPointGraphTestCase ());
  AddTestCase (new MultiGraphTestCase ());
}

BaseTopologyTestSuite baseTopologyTestSuite;

} // namespace eecityu
} // namespace ns3
